package base

import (
	"bytes"
	"encoding/json"
	"fmt"
	"math"
	"math/rand"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// 用于获取随机字符串
const (
	KCRandKindNum   = 0 // 纯数字
	KCRandKindLower = 1 // 小写字母
	KCRandKindUpper = 2 // 大写字母
	KCRandKindAll   = 3 // 数字、大小写字母
)

// GetPlaceholderStrForNum 获取指定位数占位符的字符串
func GetPlaceholderStrForNum(size int, num int64, placeholder ...byte) string {
	// 设置默认占位符位 0
	var defaultPlaceholder byte = '0'
	if len(placeholder) > 0 {
		defaultPlaceholder = placeholder[0]
	}
	if size <= 0 {
		size = 7 // 总共有7位字符传即 "0000000"
	}
	maxNum := math.Pow10(size - 1)
	// 如果传入的参数大于占位符生成的数-1就直接返回该数字符串, 即: 占位数位3, 如果 num > 10^2 就直接返回
	if num >= int64(maxNum) {
		return ToString(num)
	}
	numStr := ToString(num)
	buf := new(strings.Builder)
	lenNumStr := len(numStr)
	for i := size; i > 0; i-- {
		// 如果相等就替换为数字字符串
		if i == lenNumStr {
			buf.WriteString(numStr)
			break
		}
		buf.WriteByte(defaultPlaceholder)
	}
	return buf.String()
}

// PriceStr2Str 价格处理, 将价格: 元=>分
func PriceStr2Str(value string) (ans string) {
	n := len(value)
	if n == 0 {
		return "0"
	} else if n > 3 && value[n-3] == '.' {
		return value
	}
	op := ""
	if value[0] == '-' {
		op = "-"
		value = value[1:]
		n = n - 1
	}
	switch n {
	case 0:
		ans = "0"
	case 1:
		ans = "0.0" + value
	case 2:
		ans = "0." + value
	default:
		ans = value[:n-2] + "." + value[n-2:]
	}
	return op + ans
}

// Deprecated
// GetJsonPrintStr 获取打印 json 串, 推荐使用: GetJson2Dump
func GetJsonPrintStr(v interface{}) string {
	b, err := json.MarshalIndent(v, "", "\t")
	if err != nil {
		return ToString(v)
	}
	return string(b)
}

// GetJson2Dump 获取待打印的内容
func GetJson2Dump(data interface{}) string {
	b, _ := json.MarshalIndent(data, "", "\t")
	return string(b)
}

// DistinctIdsStr 将输入拼接 id 参数按照指定字符进行去重, 如:
// DistinctIdsStr("12345,123,20,123,20,15", ",")
// => 12345,123,20,15
func DistinctIdsStr(s string, split string) string {
	strLen := len(s)
	if strLen == 0 {
		return s
	}

	distinctMap := make(map[string]string, strLen/2)
	sortSlice := make([]string, 0, strLen/2) // 用于保证输出顺序
	saveFunc := func(val string) {
		val = strings.Trim(val, " ")
		if _, ok := distinctMap[val]; !ok {
			distinctMap[val] = val
			sortSlice = append(sortSlice, val)
		}
	}

	for {
		index := strings.Index(s, split)
		if index < 0 {
			// 这里需要处理最后一个字符
			saveFunc(s)
			break
		}
		saveFunc(s[:index])
		s = s[index+1:]

		// 这样可以防止最后一位为 split 字符, 到时就会出现一个空
		if s == "" {
			break
		}
	}
	buf := new(strings.Builder)
	buf.Grow(strLen / 2)
	lastIndex := len(sortSlice) - 1
	for index, val := range sortSlice {
		v := distinctMap[val]
		if index < lastIndex {
			buf.WriteString(v + split)
		} else {
			buf.WriteString(v)
		}
	}
	return buf.String()
}

// GetShortStrForRand 随机字符串, 默认按照: 数字、大小写字母进行处理
func GetShortStrForRand(size int, kind ...int) string {
	// 通过 ASCII, [2]int{起始值, 长度}
	kinds := [3][2]int{{48, 10}, {97, 26}, {65, 26}}
	result := make([]byte, size)
	newKind := KCRandKindAll
	if len(kind) > 0 {
		newKind = kind[0]
	}
	isAll := newKind > 2 || newKind < 0
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < size; i++ {
		if isAll { // random ikind
			newKind = rand.Intn(3)
		}
		startVal, scope := kinds[newKind][0], kinds[newKind][1]
		result[i] = byte(startVal + rand.Intn(scope))
		time.Sleep(1 * time.Microsecond)
	}
	return string(result)
}

// Str 将内容转为 string
func Str(src interface{}) string {
	return ToString(src)
}

// ToString 将内容转为 string, 性能是优于 fmt.Sprintf
func ToString(src interface{}, jsonEscapeHTML ...bool) string {
	if src == nil {
		return ""
	}
	switch value := src.(type) {
	case int:
		return strconv.Itoa(value)
	case int8:
		return strconv.Itoa(int(value))
	case int16:
		return strconv.Itoa(int(value))
	case int32:
		return strconv.Itoa(int(value))
	case int64:
		return strconv.FormatInt(value, 10)
	case uint:
		return strconv.FormatUint(uint64(value), 10)
	case uint8:
		return strconv.FormatUint(uint64(value), 10)
	case uint16:
		return strconv.FormatUint(uint64(value), 10)
	case uint32:
		return strconv.FormatUint(uint64(value), 10)
	case uint64:
		return strconv.FormatUint(value, 10)
	case float32:
		return strconv.FormatFloat(float64(value), 'f', -1, 32)
	case float64:
		return strconv.FormatFloat(value, 'f', -1, 64)
	case bool:
		return strconv.FormatBool(value)
	case string:
		return value
	case []byte:
		return string(value)
	case time.Time:
		if value.IsZero() {
			return ""
		}
		return value.String()
	case *time.Time:
		if value == nil {
			return ""
		}
		return value.String()
	default:
		rv := reflect.ValueOf(value)
		kind := rv.Kind()
		switch kind {
		case reflect.Chan,
			reflect.Map,
			reflect.Slice,
			reflect.Func,
			reflect.Ptr,
			reflect.Interface,
			reflect.UnsafePointer:
			if rv.IsNil() {
				return ""
			}
		}
		if kind == reflect.Ptr {
			return ToString(rv.Elem().Interface())
		}

		// 其他的用 josn 序列化下
		buf := new(strings.Builder)
		encoder := json.NewEncoder(buf)
		if len(jsonEscapeHTML) > 0 {
			encoder.SetEscapeHTML(jsonEscapeHTML[0])
		}
		if err := encoder.Encode(value); err != nil {
			return fmt.Sprint(value)
		} else {
			return strings.TrimSuffix(buf.String(), "\n")
		}
	}
}

// InString 目标是否在这些内容中
func InString(target string, ins ...string) bool {
	for _, in := range ins {
		if target == in {
			return true
		}
	}
	return false
}

// Escape 转义字符串
func Escape(src string) string {
	pos := 0
	vLen := len(src)
	buf := make([]byte, vLen*2)
	for i := 0; i < vLen; i++ {
		v := src[i]
		switch v {
		case '\'':
			buf[pos] = '\\'
			buf[pos+1] = '\''
			pos += 2
		case '"':
			buf[pos] = '\\'
			buf[pos+1] = '"'
			pos += 2
		case '\x00':
			buf[pos] = '\\'
			buf[pos+1] = '0'
			pos += 2
		case '\n':
			buf[pos] = '\\'
			buf[pos+1] = 'n'
			pos += 2
		case '\r':
			buf[pos] = '\\'
			buf[pos+1] = 'r'
			pos += 2
		case '\t':
			buf[pos] = '\\'
			buf[pos+1] = 't'
			pos += 2
		case '\x1a':
			buf[pos] = '\\'
			buf[pos+1] = 'Z'
			pos += 2
		case '\\':
			buf[pos] = '\\'
			buf[pos+1] = '\\'
			pos += 2
		default:
			buf[pos] = v
			pos++
		}
	}
	return string(buf[:pos])
}

func Str2Int(str string) int {
	num, _ := strconv.Atoi(str)
	return num
}

// MarshalUnEscape json marshal 不专义
func MarshalUnEscape(src interface{}) ([]byte, error) {
	buf := new(bytes.Buffer)
	encoder := json.NewEncoder(buf)
	encoder.SetEscapeHTML(false)
	if err := encoder.Encode(src); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
